#pragma once

#include "PacketCapture.h"
#include "Socket.h"

/////////////////////////////////////////////////
/// \class SendPacket
///
/// \tparam PacketFilter the packet filter type,
///         should implement the Stop() function
///
/// \brief Send captured packets to client
///
/// Packets can be sent to client by this class.
/// Contains a packet filter do determine whether
/// a packet should be sent to client.
/////////////////////////////////////////////////
template<typename PacketFilter>
	class SendPacket
{
public:

	/////////////////////////////////////////////////
	/// \fn SendPacket(Socket &sock, PacketFilter &packetFilter)
	/// \param sock The client socket
	/// \param packetFilter The packet filter which implemented
	///        the Stop() function
	///
	/// Create the packet sender.
	/////////////////////////////////////////////////
	SendPacket(Socket &sock, PacketFilter &packetFilter)
		: m_socket(sock)
		, m_pPacketCapture(NULL)
		, m_packetFilter(packetFilter)
	{
	}

	virtual ~SendPacket()
	{
	}

	/////////////////////////////////////////////////
	/// \fn void Init()
	/// \param pPacketCapture The packet capture
	///
	/// Initialize the packet sender with a packet capture.
	/// A packet sender must be initialized before doing other operations.
	/////////////////////////////////////////////////
	void Init(PacketCapture<SendPacket> *pPacketCapture)
	{
		m_pPacketCapture = pPacketCapture;
	}

	/////////////////////////////////////////////////
	/// \fn void operator()(const pcap_pkthdr *h, const u_char *bytes)
	/// \param h The packet information, declared in libpcap
	/// \param bytes The whole packet data
	///
	/// Send the packet to the client using the client socket
	/////////////////////////////////////////////////
	void operator()(const pcap_pkthdr *h, const u_char *bytes)
	{
		sockaddr_in sockaddr = {0}, peeraddr = {0};

		// Get the local sock address of the client socket
		if(m_socket.GetSockAddr(sockaddr) != 0)
		{
			m_pPacketCapture->Stop();
			return;
		}

		// Get the remote peer address of the client socket
		if(m_socket.GetPeerAddr(peeraddr) != 0)
		{
			m_pPacketCapture->Stop();
			return;
		}

		// Filter packet
		if(!m_packetFilter(h, bytes, sockaddr, peeraddr))
			return;

		// Send packet length to client
		int sendlen = sizeof(h->caplen);
		int caplen = ntohl(h->caplen);
		if(m_socket.Send(&caplen, &sendlen) != 0)
		{
			m_pPacketCapture->Stop();
			return;
		}
		if(sendlen < 1)
		{
			m_pPacketCapture->Stop();
			return;
		}

		// Send packet data to client
		sendlen = h->caplen;
		if(m_socket.Send(bytes, &sendlen) != 0)
		{
			m_pPacketCapture->Stop();
			return;
		}
		if(sendlen < 1)
		{
			m_pPacketCapture->Stop();
			return;
		}
	}
private:
	Socket &m_socket;	/// The client socket
	PacketCapture<SendPacket> *m_pPacketCapture;	/// The packet capturer
	PacketFilter &m_packetFilter;	/// The packet filter
};
